/**
* \file DGVMainVTK.cpp
*
* \brief This file defines a class for the Main Window in DGV applications.
* \author Shekhar S. Chandra, 2008
*
* This file is part of DGV Library.
*
* DGV is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DGV is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DGV.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "DGVMainVTK.h"

#include <QFileDialog>

#include "vtkWindowToImageFilter.h"
#include "vtkPNGWriter.h"
#include "vtkJPEGWriter.h"
#include "vtkTIFFWriter.h"
#include "vtkPNMWriter.h"
#include "vtkBMPWriter.h"
#include "vtkPostScriptWriter.h"
#include "vtkXMLImageDataWriter.h"

//Forms
#include "ui/OperateForm.h"
#include "ui/AboutForm.h"

DGVMainVTK::DGVMainVTK(QWidget *parent) : QMainWindow(parent)
{
    QString tmp;

    workingOn = false;
    conductingFFT = false;
    conductingFFT_1D = false;
    conductingInverseFFT = false;
    conductingInverseFFT_1D = false;

    ///Establish a workspace for user
    workspaces = new QTabWidget(this);
    workspaces->setTabsClosable(true);
    newTab();
    QMainWindow::setCentralWidget(workspaces);
    windowMapper = new QSignalMapper(this);

    ///Setup Console
    console = new DGVConsole(this);
    addDockWidget(Qt::RightDockWidgetArea,console);
    console->setWindowTitle("Console");
    console->show();
    console->printWarning("Discrete Geometry Viewer (DGV) Version: " + tmp.setNum(version));
    console->printWarning("Copyright Shekhar S. Chandra, 2008-2010.");
    console->printWarning("Ready for display and analysis.");

    ///Transform setup
    Transforms = new DGVTransform(this);
    Transforms->alwaysWaitTillComplete(false); ///\todo Make into threads
    Transforms->setConsole(console);

    ///Setup the UI Menus
    createMenu();

    ///Setup Connections
    createConnections();

    setAcceptDrops(true);

    setToolTip("<p style='white-space:pre'>Welcome to DGV. Use the <b>context menu</b> (right click) for all operations.\n Load data from the <b>File Menu</b>.</p>");

    readSettings();
}

DGVMainVTK::~DGVMainVTK()
{
    contextMenusToPass.clear();

    //delete Transforms;
}

void DGVMainVTK::addImage(DGVImageVTK *img)
{
    qobject_cast<QWorkspace *>(workspaces->currentWidget())->addWindow(img);
    img->addToContextMenu(contextMenusToPass);
    img->setConsole(console);
    commonChildProperties(img);
    connect(img, SIGNAL(nameChanged(const QString &)), this, SLOT(setTabName(const QString &)));
    connect(img, SIGNAL(tableAvailable(DGVTableVTK *)), this, SLOT(displayTable(DGVTableVTK *)));
    connect(img, SIGNAL(surfacePlotAvailable(DGVSurfacePlotVTK *)), this, SLOT(displaySurfacePlot(DGVSurfacePlotVTK *)));
    connect(img, SIGNAL(plotAvailable(DGVPlot *)), this, SLOT(displayPlot(DGVPlot *)));
    connect(img, SIGNAL(plotAvailable(DGVPlotVTK *)), this, SLOT(displayPlot(DGVPlotVTK *)));
    connect(img, SIGNAL(recentFileAvailable(const QString &)), this, SLOT(setCurrentFile(const QString &)));
    //connect(img, SIGNAL(volumePlotAvailable(DGVVolumePlotVTK *)), this, SLOT(displayVolumePlot(DGVVolumePlotVTK *)));
}

void DGVMainVTK::addImage(DGVImage *img)
{
    qobject_cast<QWorkspace *>(workspaces->currentWidget())->addWindow(img);
    commonChildProperties(img);
    connect(img, SIGNAL(tableAvailable(DGVTable *)), this, SLOT(displayTable(DGVTable *)));
}

void DGVMainVTK::addTable(DGVTable *tbl)
{
    qobject_cast<QWorkspace *>(workspaces->currentWidget())->addWindow(tbl);
    commonChildProperties(tbl);
    connect(tbl, SIGNAL(plotAvailable(DGVPlot *)), this, SLOT(displayPlot(DGVPlot *)));
}

void DGVMainVTK::addTable(DGVTableVTK *tbl)
{
    qobject_cast<QWorkspace *>(workspaces->currentWidget())->addWindow(tbl);
    tbl->addToContextMenu(contextMenusToPass);
    tbl->setConsole(console);
    commonChildProperties(tbl);
    if(!tbl->isLinkedToImage())
        connect(tbl, SIGNAL(cellChanged(int,int)), tbl, SLOT(updateData(int,int)));
    connect(tbl, SIGNAL(nameChanged(const QString &)), this, SLOT(setTabName(const QString &)));
    connect(tbl, SIGNAL(plotAvailable(DGVPlot *)), this, SLOT(displayPlot(DGVPlot *)));
    connect(tbl, SIGNAL(animationAvailable(DGVAnimatedScan *)), this, SLOT(displayScan(DGVAnimatedScan *)));
    connect(tbl, SIGNAL(surfacePlotAvailable(DGVSurfacePlotVTK *)), this, SLOT(displaySurfacePlot(DGVSurfacePlotVTK *)));
    connect(tbl, SIGNAL(fftSelectionAvailable(Array<Array<complex<tableType>,1>,1> )), this, SLOT(FFT_1D(Array<Array<complex<tableType>,1>,1> )));
    connect(tbl, SIGNAL(ifftSelectionAvailable(Array<Array<complex<tableType>,1>,1> )), this, SLOT(iFFT_1D(Array<Array<complex<tableType>,1>,1> )));
    connect(tbl, SIGNAL(fftRealSelectionAvailable(Array<Array<tableType,1>,1> )), this, SLOT(FFT_Real_1D(Array<Array<tableType,1>,1> )));
    connect(tbl, SIGNAL(ifftRealSelectionAvailable(Array<Array<complex<tableType>,1>,1> )), this, SLOT(iFFT_Real_1D(Array<Array<complex<tableType>,1>,1> )));
    connect(tbl, SIGNAL(recentFileAvailable(const QString &)), this, SLOT(setCurrentFile(const QString &)));
}

void DGVMainVTK::addPlot(DGVPlot *plot)
{
    qobject_cast<QWorkspace *>(workspaces->currentWidget())->addWindow(plot);
    commonChildProperties(plot);
    //connect(img, tableAvailable(DGVTable *), this, displayTable(DGVTable *));
    connect(plot, SIGNAL(nameChanged(const QString &)), this, SLOT(setTabName(const QString &)));
}

void DGVMainVTK::addPlot(DGVPlotVTK *plot)
{
    qobject_cast<QWorkspace *>(workspaces->currentWidget())->addWindow(plot);
    commonChildProperties(plot);
    //connect(img, tableAvailable(DGVTable *), this, displayTable(DGVTable *));
}

void DGVMainVTK::addScan(DGVAnimatedScan *scan)
{
    qobject_cast<QWorkspace *>(workspaces->currentWidget())->addWindow(scan);
    commonChildProperties(scan);
}

void DGVMainVTK::addSurfacePlot(DGVSurfacePlotVTK *plot)
{
    qobject_cast<QWorkspace *>(workspaces->currentWidget())->addWindow(plot);
    plot->setConsole(console);
    commonChildProperties(plot);
    connect(plot, SIGNAL(nameChanged(const QString &)), this, SLOT(setTabName(const QString &)));
}

void DGVMainVTK::addVolumePlot(DGVVolumePlotVTK *plot)
{
    qobject_cast<QWorkspace *>(workspaces->currentWidget())->addWindow(plot);
    commonChildProperties(plot);
    connect(plot, SIGNAL(surfacePlotAvailable(DGVSurfacePlotVTK *)), this, SLOT(displaySurfacePlot(DGVSurfacePlotVTK *)));
    connect(plot, SIGNAL(nameChanged(const QString &)), this, SLOT(setTabName(const QString &)));
}

void DGVMainVTK::addLattice(DGVLatticeVTK *lattice)
{
    qobject_cast<QWorkspace *>(workspaces->currentWidget())->addWindow(lattice);
    commonChildProperties(lattice);
}

bool DGVMainVTK::loadFile(const QString &filename)
{
    bool success = false;

    if (!filename.isEmpty())
    {
        working();
        if(filename.contains(".png", Qt::CaseInsensitive)
            || filename.contains(".pnm", Qt::CaseInsensitive) || filename.contains(".ppm", Qt::CaseInsensitive) || filename.contains(".pgm", Qt::CaseInsensitive) || filename.contains(".pbm", Qt::CaseInsensitive)
            || filename.contains(".jpeg", Qt::CaseInsensitive) || filename.contains(".jpg", Qt::CaseInsensitive)
            || filename.contains(".bmp", Qt::CaseInsensitive)
            || filename.contains(".tiff", Qt::CaseInsensitive) || filename.contains(".tif", Qt::CaseInsensitive)
            || filename.contains(".dicom", Qt::CaseInsensitive) || filename.contains(".dcm", Qt::CaseInsensitive)
            || filename.contains(".vti", Qt::CaseInsensitive) || filename.contains(".vtk", Qt::CaseInsensitive)
            || filename.contains(".tga", Qt::CaseInsensitive) )
        {
            DGVImageVTK *image = new DGVImageVTK(this);

            console->printInfo("Opening Image: " + filename);
            connect(image, SIGNAL(volumePlotAvailable(DGVVolumePlotVTK *)), this, SLOT(displayVolumePlot(DGVVolumePlotVTK *))); //!< DO NOT MOVE!
            image->setConsole(console);
            if(image->openImage(filename))
            {
                displayImage(image);
                success = true;
            }
            else
                console->printError("An error was enountered while loading the image " + filename);
        }
        else if(filename.contains(".csv", Qt::CaseInsensitive))
        {
            DGVTableVTK *table = new DGVTableVTK(this);

            console->printInfo("Opening Table: " + filename);
            table->setConsole(console);
            if(table->loadCSV(filename))
            {
                table->setName(filename);
                displayTable(table);
                success = true;
            }
            else
                console->printError("Unable to load data file " + filename + ". Check the data Delimiters and Try opening the file again.");
        }
        else if(filename.contains(".dat", Qt::CaseInsensitive) || filename.contains(".gnu", Qt::CaseInsensitive) || filename.contains(".gnuplot", Qt::CaseInsensitive))
        {
            DGVPlot *gnuPlot = new DGVPlot(this);

            if(gnuPlot->loadGNU(filename))
            {
                displayPlot(gnuPlot);
                success = true;
            }
            else
            {
                console->printError("Reading of the file did not occur because there was an error.");
                console->printError("Check the contents of the file if you did not cancel the operation.");
                success = false;
            }
        }
        else if(filename.contains(".dio", Qt::CaseInsensitive))
        {
            DGVDiophantineVTK dioFile;
            DGVLatticeVTK *lattice;

            console->printInfo("Opening Diophantine File: " + filename);
            lattice = dioFile.loadFile(filename); //!< \todo Bug: File existence not checked.
            lattice->setName(filename);
            displayLattice(lattice);
            success = true;
        }
        else if(filename.contains(".blz", Qt::CaseInsensitive) || filename.contains(".blitz", Qt::CaseInsensitive))
        {
            ifstream inFile(filename.toStdString().c_str(),ios::in);
            string tmpStr1, tmpStr2;
            DGVImageVTK *blitzImage = new DGVImageVTK(this);

            inFile >> tmpStr1 >> tmpStr2;

            if(tmpStr2 == "Yes")
            {
                blitzImage->complexValuedMode(true);
                inFile >> blitzImage->dataComplex();
            }
            else
            {
                blitzImage->complexValuedMode(false);
                inFile >> blitzImage->data();
            }
            blitzImage->setName(filename);
            blitzImage->generateImage();
            displayImage(blitzImage);

            inFile.close();
            success = true;
        }
        else
        {
            console->printError("File Type in: " + filename + " not supported! Check extension.");
            success = false;
        }
        if(success)
            setCurrentFile(filename);
        done();
    }
    return success;
}

void DGVMainVTK::loadFiles(const QStringList &filenames)
{
    for(int j = 0; j < filenames.size(); j ++)
        loadFile(filenames[j]); //Errors handled internally
}

bool DGVMainVTK::loadStack(const QString &filename)
{
    bool success = false;

    if (!filename.isEmpty())
    {
        working();
        if(filename.contains(".png", Qt::CaseInsensitive)
            || filename.contains(".pnm", Qt::CaseInsensitive) || filename.contains(".ppm", Qt::CaseInsensitive) || filename.contains(".pgm", Qt::CaseInsensitive) || filename.contains(".pbm", Qt::CaseInsensitive)
            || filename.contains(".jpeg", Qt::CaseInsensitive) || filename.contains(".jpg", Qt::CaseInsensitive)
            || filename.contains(".bmp", Qt::CaseInsensitive)
            || filename.contains(".tiff", Qt::CaseInsensitive) || filename.contains(".tif", Qt::CaseInsensitive))
        {
            DGVImageVTK *image = new DGVImageVTK(this);

            console->printInfo("Opening Image: " + filename);
            connect(image, SIGNAL(volumePlotAvailable(DGVVolumePlotVTK *)), this, SLOT(displayVolumePlot(DGVVolumePlotVTK *))); //!< DO NOT MOVE!
            image->setConsole(console);
            if(image->openStack(filename))
            {
                displayImage(image);
                success = true;
            }
            else
                console->printError("An error was enountered while loading the image " + filename);
        }
        done();
    }
    if(success)
        setCurrentFile(filename);

    return success;
}

Array<imageType,2>* DGVMainVTK::activeData()
{
    if(qobject_cast<QWorkspace *>(workspaces->currentWidget())->activeWindow() == 0)
    {
        ///\todo Possible Bug: Handling when no window active.
        return 0;
    }
    else
    {
        if(isActiveImage())
        {
            DGVImageVTK *childImg = activeImage();
            return &childImg->data();
        }
        else if(isActiveTable())
        {
            DGVTableVTK *childTbl = activeTable();
            return &childTbl->data();
        }
        else if(isActiveSPlot())//!< \todo Check else statement here
        {
            DGVSurfacePlotVTK *childSPlt = activeSPlot();
            return &childSPlt->data();
        }
        else
            return NULL;
    }
}

Array<imageType,2>* DGVMainVTK::activeViewedData()
{
    if(qobject_cast<QWorkspace *>(workspaces->currentWidget())->activeWindow() == 0)
    {
        ///\todo Possible Bug: Handling when no window active.
        return 0;
    }
    else
    {
        if(isActiveImage())
        {
            DGVImageVTK *childImg = activeImage();
            return &childImg->dataViewing();
        }
        else if(isActiveTable())
        {
            DGVTableVTK *childTbl = activeTable();
            return &childTbl->dataViewing();
        }
        else if(isActiveSPlot())//!< \todo Check else statement here
        {
            DGVSurfacePlotVTK *childSPlt = activeSPlot();
            return &childSPlt->data();
        }
        else
            return NULL;
    }
}

Array<complex<imageType>,2>* DGVMainVTK::activeDataComplex()
{
    if(qobject_cast<QWorkspace *>(workspaces->currentWidget())->activeWindow() == 0)
    {
        ///\todo Possible Bug: Handling when no window active.
        return 0;
    }
    else
    {
        if(isActiveImage())
        {
            DGVImageVTK *childImg = activeImage();
            return &childImg->dataComplex();
        }
        else if(isActiveTable())
        {
            DGVTableVTK *childTbl = activeTable();
            return &childTbl->dataComplex();
        }
        else
            return NULL;
    }
}

bool DGVMainVTK::isActiveDataComplex()
{
    if(isActiveImage())
    {
        DGVImageVTK *childImg = activeImage();
        return childImg->isComplexValued();
    }
    else if(isActiveTable())
    {
        DGVTableVTK *childTbl = activeTable();
        return childTbl->isComplexValued();
    }
    else
        return false;
}

QString DGVMainVTK::activeName()
{
    if(qobject_cast<QWorkspace *>(workspaces->currentWidget())->activeWindow() == 0)
    {
        ///\todo Possible Bug: Handling when no window active.
        return 0;
    }
    else
    {
        if(isActiveImage())
        {
            DGVImageVTK *childImg = activeImage(); ///Check for image validity already done so safely do...
            return childImg->strippedName();
        }
        else if(isActiveTable())
        {
            DGVTableVTK *childTbl = activeTable(); ///Check for image validity already done so safely do...
            return childTbl->strippedName();
        }
        else if(isActiveSPlot())
        {
            DGVSurfacePlotVTK *childSPlt = activeSPlot(); ///Check for image validity already done so safely do...
            return childSPlt->strippedName();
        }
        else if(isActiveVPlot())
        {
            DGVVolumePlotVTK *childVPlt = activeVPlot(); ///Check for image validity already done so safely do...
            return childVPlt->strippedName();
        }
        else if(isActivePlot())
        {
            DGVPlot *childPlt = activePlot(); ///Check for image validity already done so safely do...
            return childPlt->strippedName();
        }
        else
            return "Unrecognised Display";
    }
}

QString DGVMainVTK::activeNamePrefix()
{
    if(qobject_cast<QWorkspace *>(workspaces->currentWidget())->activeWindow() == 0)
    {
        ///\todo Possible Bug: Handling when no window active.
        return 0;
    }
    else
    {
        if(isActiveImage())
        {
            DGVImageVTK *childImg = activeImage(); ///Check for image validity already done so safely do...
            return childImg->strippedNamePrefix();
        }
        else if(isActiveTable())
        {
            DGVTableVTK *childTbl = activeTable(); ///Check for image validity already done so safely do...
            return childTbl->strippedNamePrefix();
        }
        else if(isActiveSPlot())
        {
            DGVSurfacePlotVTK *childSPlt = activeSPlot(); ///Check for image validity already done so safely do...
            return childSPlt->strippedNamePrefix();
        }
        else if(isActiveVPlot())
        {
            DGVVolumePlotVTK *childVPlt = activeVPlot(); ///Check for image validity already done so safely do...
            return childVPlt->strippedNamePrefix();
        }
        else if(isActivePlot())
        {
            DGVPlot *childPlt = activePlot(); ///Check for image validity already done so safely do...
            return childPlt->strippedNamePrefix();
        }
        else
            return "Unrecognised Display";
    }
}

//Slots
void DGVMainVTK::displayImage(DGVImageVTK *newImage)
{
    working();
    ///Set the image into the viewer
    addImage(newImage);
    console->printMessage("Displaying " + newImage->strippedNamePrefix());
    newImage->setToolTip("<p style='white-space:pre'>VTK Image Viewer 2 Keys - <b>f:</b> Move to, <b>r:</b> Reset, <b>Shift+r:</b> Reset Camera,\n<b>Shift+Mouse1:</b> Translate, <b>Shift+Ctrl+Mouse1:</b> Zoom</p>");
    //newImage->setWindowFlags(Qt::WindowShadeButtonHint);
    newImage->show();
    done();
}

void DGVMainVTK::displayImage(DGVImage *newImage)
{
    working();
    ///Set the image into the viewer
    addImage(newImage);
    console->printMessage("Displaying " + newImage->strippedNamePrefix());
    newImage->show();
    done();
}

void DGVMainVTK::displayTable(DGVTable *newTable)
{
    working();
    ///Set the table into the viewer
    //newTable->resizeColumnsToContents();
    //newTable->adjustSize();
    addTable(newTable);
    console->printMessage("Displaying " + newTable->strippedNamePrefix());
    newTable->show();
    done();
}

void DGVMainVTK::displayTable(DGVTableVTK *newTable)
{
    working();
    ///Set the table into the viewer
    //newTable->resizeColumnsToContents(); //!Slows program down
    //newTable->adjustSize();
    addTable(newTable);
    console->printMessage("Displaying " + newTable->strippedNamePrefix());
    newTable->setToolTip("<p style='white-space:pre'>Use the <b>context menu</b> (right click) for all operations.</p>");
    newTable->show();
    done();
}

void DGVMainVTK::displayPlot(DGVPlot *newPlot)
{
    working();
    ///Set the plot into the viewer
    addPlot(newPlot);
    console->printMessage("Displaying " + newPlot->strippedNamePrefix());
    newPlot->setToolTip("<p style='white-space:pre'>Use the <b>context menu</b> (right click) for all operations.</p>");
    newPlot->show();
    done();
}

void DGVMainVTK::displayPlot(DGVPlotVTK *newPlot)
{
    working();
    ///Set the plot into the viewer
    addPlot(newPlot);
    console->printMessage("Displaying " + newPlot->strippedNamePrefix());
    newPlot->show();
    done();
}

void DGVMainVTK::displayScan(DGVAnimatedScan *newScan)
{
    working();
    ///Set the plot into the viewer
    addScan(newScan);
    console->printMessage("Displaying " + newScan->strippedNamePrefix());
    newScan->show();
    done();
}

void DGVMainVTK::displaySurfacePlot(DGVSurfacePlotVTK *newPlot)
{
    working();
    ///Set the surface plot into the viewer
    addSurfacePlot(newPlot);
    console->printMessage("Displaying " + newPlot->strippedNamePrefix());
    newPlot->setToolTip("<p style='white-space:pre'>VTK Render Window Keys - <b>f:</b> Move to, <b>r:</b> Reset, <b>Shift+r:</b> Reset Camera,\n<b>Shift+Mouse1:</b> Translate, <b>Shift+Ctrl+Mouse1:</b> Zoom</p>");
    newPlot->show();
    done();
}

void DGVMainVTK::displayVolumePlot(DGVVolumePlotVTK *newPlot)
{
    working();
    ///Set the volume plot into the viewer
    addVolumePlot(newPlot);
    console->printMessage("Displaying " + newPlot->strippedNamePrefix());
    newPlot->setToolTip("<p style='white-space:pre'>VTK Render Window Keys - <b>f:</b> Move to, <b>r:</b> Reset, <b>Shift+r:</b> Reset Camera,\n<b>Shift+Mouse1:</b> Translate, <b>Shift+Ctrl+Mouse1:</b> Zoom</p>");
    newPlot->show();
    done();
}

void DGVMainVTK::displayLattice(DGVLatticeVTK *newLattice)
{
    working();
    ///Set the volume plot into the viewer
    addLattice(newLattice);
    console->printMessage("Displaying Lattice: " + newLattice->strippedName());
    newLattice->setToolTip("<p style='white-space:pre'>VTK Render Window Keys - <b>f:</b> Move to, <b>r:</b> Reset, <b>Shift+r:</b> Reset Camera,\n<b>Shift+Mouse1:</b> Translate, <b>Shift+Ctrl+Mouse1:</b> Zoom</p>");
    newLattice->show();
    done();
}

void DGVMainVTK::createImage(Array<imageType,2> newData, QString imgName)
{
    DGVImageVTK *image = new DGVImageVTK(this);

    console->printInfo("Generating Image and Displaying Result: " + imgName);
    image->setName(imgName);
    image->setConsole(console);
    if(actionCenterDC->isChecked() && conductingFFT)
    {
        console->printInfo("Shifting DC: Check alignment for odd arrays sizes.");
        Transforms->shiftDC(newData);
        conductingFFT = false;
        conductingInverseFFT = false;
    }
    if(actionNormalise->isChecked() && conductingInverseFFT)
    {
        console->printInfo("Normalising FFT.");
        Transforms->normalise(newData);
        conductingInverseFFT = false;
    }
    image->setData(newData);
    image->generateImage();
    console->printMessage("Done Generating");

    displayImage(image);
}

void DGVMainVTK::createImage(Array< complex<imageType>,2 > newData, QString imgName)
{
    DGVImageVTK *image = new DGVImageVTK(this);

    console->printInfo("Generating Image and Displaying Result: " + imgName);
    image->setName(imgName);
    image->setConsole(console);
    if(actionCenterDC->isChecked() && conductingFFT)
    {
        console->printInfo("Shifting DC: Check alignment for odd arrays sizes.");
        Transforms->shiftDC(newData);
        conductingFFT = false;
        conductingInverseFFT = false;
    }
    if(actionNormalise->isChecked() && conductingInverseFFT)
    {
        console->printInfo("Normalising FFT.");
        Transforms->normalise(newData);
        conductingInverseFFT = false;
    }
    image->setData(newData);
    image->generateImage();
    console->printMessage("Done Generating");

    displayImage(image);
}

void DGVMainVTK::createTable(Array<imageType,2> newData, QString tblName)
{
    DGVTableVTK *tbl = new DGVTableVTK(this);

    console->printInfo("Generating Table and Displaying Result");
    tbl->setName(tblName);
    tbl->setConsole(console);
    ///DC Shift not necessary for Real FFT inversion
    if(actionNormalise->isChecked() && conductingInverseFFT_1D)
    {
        console->printInfo("Normalising 1D FFT.");
        //Transforms->normalise(newData); //Doesn't work well for multiple 1D FFTs
        if(newData.cols() > newData.rows())
            newData /= newData.cols();
        else
            newData /= newData.rows();
        conductingInverseFFT_1D = false;
    }
    tbl->setData(newData);
    console->printMessage("Done Generating");

    displayTable(tbl);
}

void DGVMainVTK::createTable(Array< complex<imageType>,2 > newData, QString tblName)
{
    DGVTableVTK *tbl = new DGVTableVTK(this);

    console->printInfo("Generating Complex Table and Displaying Result");
    tbl->setName(tblName);
    tbl->setConsole(console);
    if(actionCenterDC->isChecked() && conductingFFT_1D)
    {
        console->printInfo("Shifting DC: Check alignment for odd arrays sizes.");
        Transforms->shiftDC(newData);
        conductingFFT_1D = false;
        conductingInverseFFT_1D = false;
    }
    if(actionNormalise->isChecked() && conductingInverseFFT_1D)
    {
        console->printInfo("Normalising 1D FFT.");
        //Transforms->normalise(newData); //Doesn't work well for multiple 1D FFTs
        if(newData.cols() > newData.rows())
            newData /= newData.cols();
        else
            newData /= newData.rows();
        conductingFFT_1D = false;
        conductingInverseFFT_1D = false;
    }
    tbl->setData(newData);
    console->printMessage("Done Generating");

    displayTable(tbl);
}

void DGVMainVTK::open()
{
    QFileDialog *fileOpener = new QFileDialog(this);
    QSettings settings("Shakes", "DGV");

    QString path = settings.value("recentPath").toString();
    QStringList filenames = fileOpener->getOpenFileNames(this,
                           tr("Select File(s) to Open"),
                           path,
    tr("Images (*.png *.pnm *.ppm *.pgm *.pbm *.jpeg *.jpg *.bmp *.tiff *.tif *.tga);;DICOM Images (*.dcm *.dicom);;CSV Files (*.csv);;VTK Files (*.vti *.vtk);;GNUPlot 2D Files (*.dat *.gnu *.gnuplot);;Diophantine Files (*.dio);;Blitz Array Format (*.blz *.blitz)"));

    loadFiles(filenames);
}

void DGVMainVTK::openStack()
{
    QFileDialog *fileOpener = new QFileDialog(this);
    QSettings settings("Shakes", "DGV");

    QString path = settings.value("recentPath").toString();
    QString filename = fileOpener->getOpenFileName(this,
                           tr("Select First File in Stack"),
                           path,
    tr("Images (*.png *.pnm *.ppm *.pgm *.pbm *.jpeg *.jpg *.bmp *.tiff *.tif)"));

    loadStack(filename);
}

void DGVMainVTK::openRecentFile()
{
    QAction *action = qobject_cast<QAction *>(sender());
    if(action)
        loadFile(action->data().toString());
}

void DGVMainVTK::save()
{
    QSettings settings("Shakes", "DGV");
    QString path = settings.value("recentPath").toString(), filename;
    QWidget *activeWindow = qobject_cast<QWorkspace *>(workspaces->currentWidget())->activeWindow();

    if(!activeWindow)
        return;
    else
    {
        QFileDialog *fileSaver = new QFileDialog(this);

        if(isActiveImage() || isActiveSPlot() || isActiveVPlot() || isActiveLattice())
        {
            vtkWindowToImageFilter *windowToImage = vtkWindowToImageFilter::New();
            QVTKWidget *windowVTK = qobject_cast<QVTKWidget *>(activeWindow);

            if(windowVTK == 0)
                return;

            filename = fileSaver->getSaveFileName(this,
                                   tr("Select File Name to Save"),
                                   path,
                                   tr("Images (*.png *.pnm *.ppm *.pgm *.jpeg *.jpg *.bmp *.tiff *.tif *.vti *.ps)"));

            if (!filename.isEmpty())
            {
                QFileInfo fi(filename);
                QString extension = fi.suffix().toLower();

                windowToImage->SetInput(windowVTK->GetRenderWindow());
                windowToImage->Update();

                if(extension == "png")
                {
                    vtkPNGWriter *writer = vtkPNGWriter::New();
                    writer->SetInputConnection(windowToImage->GetOutputPort()); //Cant group outside if's GCC compiler error
                    writer->SetFileName(filename.toStdString().c_str());
                    windowVTK->GetRenderWindow()->Render();
                    writer->Write();
                    writer->Delete();
                }
                else if(extension == "jpeg" || extension == "jpg")
                {
                    vtkJPEGWriter *writer = vtkJPEGWriter::New();
                    writer->SetInputConnection(windowToImage->GetOutputPort());
                    writer->SetFileName(filename.toStdString().c_str());
                    windowVTK->GetRenderWindow()->Render();
                    writer->Write();
                    writer->Delete();
                }
                else if(extension == "tiff" || extension == "tif")
                {
                    vtkTIFFWriter *writer = vtkTIFFWriter::New();
                    writer->SetInputConnection(windowToImage->GetOutputPort());
                    writer->SetFileName(filename.toStdString().c_str());
                    windowVTK->GetRenderWindow()->Render();
                    writer->Write();
                    writer->Delete();
                }
                else if(extension == "pnm" || extension == "ppm" || extension == "pgm")
                {
                    vtkPNMWriter *writer = vtkPNMWriter::New();
                    writer->SetInputConnection(windowToImage->GetOutputPort());
                    writer->SetFileName(filename.toStdString().c_str());
                    windowVTK->GetRenderWindow()->Render();
                    writer->Write();
                    writer->Delete();
                }
                else if(extension == "bmp")
                {
                    vtkBMPWriter *writer = vtkBMPWriter::New();
                    writer->SetInputConnection(windowToImage->GetOutputPort());
                    writer->SetFileName(filename.toStdString().c_str());
                    windowVTK->GetRenderWindow()->Render();
                    writer->Write();
                    writer->Delete();
                }
                else if(extension == "ps")
                {
                    vtkPostScriptWriter *writer = vtkPostScriptWriter::New();
                    writer->SetInputConnection(windowToImage->GetOutputPort());
                    writer->SetFileName(filename.toStdString().c_str());
                    windowVTK->GetRenderWindow()->Render();
                    writer->Write();
                    writer->Delete();
                }
                else if(extension == "vti")
                {
                    vtkXMLImageDataWriter* writer = vtkXMLImageDataWriter::New();
                    writer->SetInputConnection(windowToImage->GetOutputPort());
                    writer->SetFileName(filename.toStdString().c_str());
                    writer->SetDataModeToBinary();
                    windowVTK->GetRenderWindow()->Render();
                    writer->Write();
                    writer->Delete();
                }
                else
                {
                    console->printError("Unsupported Format");
                    return;
                }
            }

            windowToImage->Delete();
        }
        else if(isActiveTable())
            activeTable()->saveAll(); ///\todo Doesnt rememember recent path.
        ///\todo isActivePlot() saving is done in DGVPlot class.
        /*else if(isActivePlot())
        {
            filename = fileSaver->getSaveFileName(this,
                                   tr("Select File Name to Save"),
                                   path,
                                   tr("Images (*.png *.ppm *.jpeg *.jpg *.bmp *.tiff *.tif);; Vector Graphics (*.svg)"));
            QFileInfo fi(filename);
            QString extension = fi.suffix().toLower();

            if (!filename.isEmpty())
            {
                if(extension == "png" || extension == "ppm" || extension == "jpeg" || extension == "jpg"
                    || extension == "bmp" || extension == "tiff" || extension == "tif")
                {
                    ///\todo Bug: Saves plots in monochrome only. Works ok in Linux though.
                    image = QPixmap::grabWidget(activeWindow);
                    image.save(filename,0,100);
                }
                else if(extension == "svg")
                {
                    QSvgGenerator svg;
                    svg.setFileName(filename);
                    svg.setSize(activePlot()->rect().size());

                    QPainter painter(&svg);
                    activePlot()->print(&painter, activePlot()->rect());
                    painter.end();
                }
            }
        }*/
        else
        {
            console->printError("Could not save active window. Active window doesn't appear to be supported.");
            return;
        }
        console->printMessage("Saved Data As: " + filename);
        setCurrentFile(filename);
    }
}

void DGVMainVTK::saveNative()
{
    QSettings settings("Shakes", "DGV");
    QString path = settings.value("recentPath").toString(), filename;
    QWidget *activeWindow = qobject_cast<QWorkspace *>(workspaces->currentWidget())->activeWindow();

    if(!activeWindow)
        return;
    else
    {
        QFileDialog *fileSaver = new QFileDialog(this);
        ofstream outFile;

        filename = fileSaver->getSaveFileName(this,
                                   tr("Select File Name to Save"),
                                   path,
                                   tr("Blitz Array Format (*.blz *.blitz)"));

        if (!filename.isEmpty())
        {
            outFile.open(filename.toStdString().c_str(),ios::out);

            outFile << "Complex: ";
            if(isActiveDataComplex())
                outFile << "Yes" << endl << *activeDataComplex();
            else
                outFile << "No" << endl << *activeData();

            outFile.close();
            console->printMessage("Saved Data As: " + filename);
            setCurrentFile(filename);
        }
    }
}

void DGVMainVTK::saveStack()
{
    QSettings settings("Shakes", "DGV");
    QString path = settings.value("recentPath").toString(), filename;
    QWidget *activeWindow = qobject_cast<QWorkspace *>(workspaces->currentWidget())->activeWindow();

    if(!activeWindow || !isActiveImage())
        return;
    else
    {
        QFileDialog *fileSaver = new QFileDialog(this);

        filename = fileSaver->getSaveFileName(this,
                                   tr("Enter First File Name (with extension)"),
                                   path,
                                   tr("Images (*.png *.pnm *.ppm *.pgm *.pbm *.jpeg *.jpg *.bmp *.tiff *.tif)"));

        if (!filename.isEmpty())
        {
            working();
            DGVImageVTK *actImage = activeImage();

            actImage->saveStack(filename);
            done();
        }
    }
}

void DGVMainVTK::newTab()
{
    QWorkspace *tmpPtr = new QWorkspace; ///\todo Workspace class is deprecated. Update to MdiArea.
    workspaces->addTab(tmpPtr, tr("Empty"));
    workspaces->setCurrentWidget(tmpPtr);
    tmpPtr->setAttribute(Qt::WA_DeleteOnClose);
    connect(tmpPtr, SIGNAL(windowActivated(QWidget *)), this, SLOT(setTabName(QWidget *)));
}

void DGVMainVTK::newTable()
{
    bool ok;
    int rows = QInputDialog::getInteger(this, tr("Enter the Number of Rows of the table."),
                                          tr("New Rows:"), 23, 0, INT_MAX, 1, &ok);
    int cols = QInputDialog::getInteger(this, tr("Enter the Number of Columns of the table."),
                                          tr("New Columns:"), 23, 0, INT_MAX, 1, &ok);
    int value = QInputDialog::getInteger(this, tr("Enter value to be applied over entire table."),
                                          tr("Value:"), 1, 0, INT_MAX, 1, &ok);

    if(ok && rows > 0 && cols > 0)
    {
        DGVTableVTK *tbl = new DGVTableVTK(this);

        tbl->setWindowTitle("New Table");
        tbl->setConsole(console);
        tbl->setDataSize(rows,cols);
        tbl->data() = static_cast<imageType>(value);
        tbl->refresh();
        displayTable(tbl);
    }
}

void DGVMainVTK::newImage()
{
    bool ok;
    int rows = QInputDialog::getInteger(this, tr("Enter the Number of Rows of the image."),
                                          tr("New Rows:"), 23, 0, INT_MAX, 1, &ok);
    int cols = QInputDialog::getInteger(this, tr("Enter the Number of Columns of the image."),
                                          tr("New Columns:"), 23, 0, INT_MAX, 1, &ok);
    int grey = QInputDialog::getInteger(this, tr("Enter the greyscale to be applied over entire image."),
                                          tr("Greyscale:"), 1, 0, INT_MAX, 1, &ok);

    if(ok && rows > 0 && cols > 0)
    {
        DGVImageVTK *image = new DGVImageVTK(this);

        image->setDataSize(rows,cols);
        image->setWindowTitle("New Image");
        image->data() = static_cast<imageType>(grey);
        image->setConsole(console);
        image->generateImage();
        displayImage(image);
    }
}

void DGVMainVTK::table()
{
    if(qobject_cast<QWorkspace *>(workspaces->currentWidget())->activeWindow() != 0)
    {
        Array<imageType,2> *tblData = activeData();
        QString nameOfData = activeName();

        createTable(*tblData,nameOfData);
    }
}

void DGVMainVTK::embed()
{
    console->printError("To be implemented");
}

void DGVMainVTK::operate()
{
    Array<imageType,2> *dataset1 = NULL, *dataset2 = NULL, result;
    Array<complex<imageType>,2> *dataset1Complex = NULL, *dataset2Complex = NULL, resultComplex;
    Array<int,1> x_sizes(4), y_sizes(4);
    OperateForm *op = new OperateForm(this);
    QComboBox *data1 = op->getComboBoxSource1();
    QComboBox *data2 = op->getComboBoxSource2();
    QComboBox *operation = op->getComboBoxOperation();
    bool acceptedOp = false, complexData1 = false, complexData2 = false, pureComplexOp = false;
    QString operateName;

    ///Populate Combo Boxes
    QWidgetList windows = qobject_cast<QWorkspace *>(workspaces->currentWidget())->windowList();

    for(int j = 0; j < windows.size(); j ++)
    {
        data1->insertItem(j,windows.at(j)->windowTitle());
        data2->insertItem(j,windows.at(j)->windowTitle());
    }

    acceptedOp = op->exec();

    if(acceptedOp)
    {
        x_sizes = 0;
        y_sizes = 0;
        qobject_cast<QWorkspace *>(workspaces->currentWidget())->setActiveWindow(windows.at(data1->currentIndex()));
        operateName += activeName();
        if(isActiveDataComplex())
        {
            if(op->onlyViewedData())
            {
                dataset1 = activeViewedData();
                x_sizes(0) = dataset1->rows();
                y_sizes(0) = dataset1->cols();
            }
            else
            {
                dataset1Complex = activeDataComplex();
                x_sizes(2) = dataset1Complex->rows();
                y_sizes(2) = dataset1Complex->cols();
                complexData1 = true;
            }
        }
        else
        {
            dataset1 = activeData();
            x_sizes(0) = dataset1->rows();
            y_sizes(0) = dataset1->cols();
        }
        if(!op->isAFunction()) ///Ignore 2nd combo box if a function is selected
        {
            qobject_cast<QWorkspace *>(workspaces->currentWidget())->setActiveWindow(windows.at(data2->currentIndex()));
            operateName += " & ";
            operateName += activeName();
            if(isActiveDataComplex())
            {
                if(op->onlyViewedData())
                {
                    dataset2 = activeViewedData();
                    x_sizes(1) = dataset2->rows();
                    y_sizes(1) = dataset2->cols();
                }
                else
                {
                    dataset2Complex = activeDataComplex();
                    x_sizes(3) = dataset2Complex->rows();
                    y_sizes(3) = dataset2Complex->cols();
                    complexData2 = true;
                }
            }
            else
            {
                dataset2 = activeData();
                x_sizes(1) = dataset2->rows();
                y_sizes(1) = dataset2->cols();
            }
        }

        if(op->isAFunction())
        {
            if(complexData1)
                resultComplex.resize(dataset1->shape());
            else
                result.resize(dataset1->shape());

            if(operation->currentText() == "sin(x)")
            {
                console->printInfo("Sine Function was selected");
                if(complexData1 && !op->onlyViewedData())
                {
                    resultComplex = sin(*(dataset1Complex));
                    createImage(resultComplex,"Sine-" + operateName);
                }
                else
                {
                    result = sin(*(dataset1));
                    createImage(result,"Sine-" + operateName);
                }
            }
            else if(operation->currentText() == "cos(x)")
            {
                console->printInfo("Cosine Function was selected");
                if(complexData1 && !op->onlyViewedData())
                {
                    resultComplex = cos(*(dataset1Complex));
                    createImage(resultComplex,"Cosine-" + operateName);
                }
                else
                {
                    result = cos(*(dataset1));
                    createImage(result,"Cosine-" + operateName);
                }
            }
            else if(operation->currentText() == "tan(x)")
            {
                console->printInfo("Tangent Function was selected");
                if(complexData1 && !op->onlyViewedData())
                {
                    resultComplex = tan(*(dataset1Complex));
                    createImage(resultComplex,"Tangent-" + operateName);
                }
                else
                {
                    result = tan(*(dataset1));
                    createImage(result,"Tangent-" + operateName);
                }
            }
            else if(operation->currentText() == "exp(x)")
            {
                console->printInfo("Exponential Function was selected");
                if(complexData1 && !op->onlyViewedData())
                {
                    resultComplex = exp(*(dataset1Complex));
                    createImage(resultComplex,"Exponent-" + operateName);
                }
                else
                {
                    result = exp(*(dataset1));
                    createImage(result,"Exponent-" + operateName);
                }
            }
            else if(operation->currentText() == "ln(1+x)")
            {
                console->printInfo("Natural Log Function was selected");
                if(complexData1 && !op->onlyViewedData())
                {
                    resultComplex = log(1.0+*(dataset1Complex));
                    createImage(resultComplex,"Natural Log-" + operateName);
                }
                else
                {
                    result = log(1.0+*(dataset1));
                    createImage(result,"Natural Log-" + operateName);
                }
            }
            else if(operation->currentText() == "log10(1+x)")
            {
                console->printInfo("Log Base 10 Function was selected");
                if(complexData1 && !op->onlyViewedData())
                {
                    resultComplex = log10(1.0+*(dataset1Complex));
                    createImage(resultComplex,"Log Base 10-" + operateName);
                }
                else
                {
                    result = log10(1.0+*(dataset1));
                    createImage(result,"Log Base 10-" + operateName);
                }
            }
            else if(operation->currentText() == "x mod M")
            {
                console->printInfo("Modulo M Function was selected");
                bool ok;
                QString tmp;

                double modulus = QInputDialog::getDouble(this, tr("Apply Modulus"), tr("Modulus: "), 256, 0, 2147483647, 1, &ok);
                console->printInfo("Modulus: " + tmp.setNum(modulus));

                if(complexData1 && !op->onlyViewedData())
                    console->printError("Cannot apply modulus to Complex Array."); ///Fmod function not allowed by blitz++ on Complex arrays
                else
                {
                    result = fmod(*(dataset1),modulus);
                    createImage(result,"(mod " + tmp.setNum(modulus));
                }
            }
        }
        else
        {
            ///Handle only one data being complex
            /// \todo Incorrect results when array sizes are unequal
            int maxRows = 0, maxCols = 0;
            for(int j = 0; j < 4; j ++)
            {
                if(x_sizes(j) > maxRows)
                    maxRows = x_sizes(j);
                if(y_sizes(j) > maxCols)
                    maxCols = y_sizes(j);
            }

            if(complexData1 || complexData2)
            {
                if(complexData1 && !complexData2)
                {
                    dataset2Complex = new Array<complex<imageType>,2>(dataset2->shape());
                    real(*dataset2Complex) = *dataset2;
                }
                else if(complexData2 && !complexData1)
                {
                    dataset1Complex = new Array<complex<imageType>,2>(dataset1->shape());
                    real(*dataset1Complex) = *dataset1;
                }
                resultComplex.resizeAndPreserve(maxRows,maxCols);
                pureComplexOp = true;
            }
            else
                result.resizeAndPreserve(maxRows,maxCols);

            if(operation->currentText() == "Addition")
            {
                console->printInfo("Addition was selected");
                if(pureComplexOp)
                {
                    resultComplex = *(dataset1Complex) + *(dataset2Complex);
                    createImage(resultComplex,"Addition-" + operateName);
                }
                else
                {
                    result = *(dataset1) + *(dataset2);
                    createImage(result,"Addition-" + operateName);
                }
            }
            else if(operation->currentText() == "Subtract")
            {
                console->printInfo("Subtract was selected");
                if(pureComplexOp)
                {
                    resultComplex = *(dataset1Complex) - *(dataset2Complex);
                    createImage(resultComplex,"Subtract-" + operateName);
                }
                else
                {
                    result = *(dataset1) - *(dataset2);
                    createImage(result,"Subtract-" + operateName);
                }
            }
            else if(operation->currentText() == "Multiply")
            {
                console->printInfo("Multiply was selected");
                if(pureComplexOp)
                {
                    resultComplex = *(dataset1Complex) * *(dataset2Complex);
                    createImage(resultComplex,"Multiply-" + operateName);
                }
                else
                {
                    result = *(dataset1) * *(dataset2);
                    createImage(result,"Multiply-" + operateName);
                }
            }
            else if(operation->currentText() == "Divide")
            {
                console->printInfo("Divide was selected");
                if(pureComplexOp)
                {
                    resultComplex = *(dataset1Complex) / *(dataset2Complex);
                    createImage(resultComplex,"Divide-" + operateName);
                }
                else
                {
                    result = *(dataset1) / *(dataset2);
                    createImage(result,"Divide-" + operateName);
                }
            }
        }
    }
}

void DGVMainVTK::FFT()
{
    working();
    conductingFFT = true;
    if(isActiveImage())
    {
        DGVImageVTK *childImg = activeImage();

        console->printInfo("FFT on " + childImg->strippedNamePrefix());
        Transforms->setName(childImg->strippedName());
        if(!childImg->isComplexValued())
        {
//            Array<complex<double>,2> field;

            field.resize(childImg->data().rows(),childImg->data().cols());
            real(field) = childImg->data();
            Transforms->fft(field);
        }
        else
            Transforms->fft(childImg->dataComplex());
        console->printMessage("FFT Complete");
    }
    else if(isActiveTable())
    {
        DGVTableVTK *childTbl = activeTable();
//        Array<complex<double>,2> field;

        field.resize(childTbl->data().rows(),childTbl->data().cols());
        real(field) = childTbl->data();

        console->printInfo("FFT on " + childTbl->strippedNamePrefix());
        Transforms->setName(childTbl->strippedName());
        Transforms->fft(field); //!< \todo Add complex data handler here for transforms
        console->printMessage("FFT Complete");
    }
    else if(isActiveSPlot())
    {
        DGVSurfacePlotVTK *childSPlt = activeSPlot();
//        Array<complex<double>,2> field;

        field.resize(childSPlt->data().rows(),childSPlt->data().cols());
        real(field) = childSPlt->data();

        console->printInfo("FFT on " + childSPlt->strippedNamePrefix());
        Transforms->setName(childSPlt->strippedName());
        Transforms->fft(field);
        console->printMessage("FFT Complete");
    }
    else if(isActiveVPlot())
    {
        console->printError("Not implemented yet");
    }
    done();
}

void DGVMainVTK::iFFT()
{
    working();
    conductingInverseFFT = true;
    if(isActiveImage())
    {
        DGVImageVTK *childImg = activeImage();

        console->printInfo("iFFT on " + childImg->strippedNamePrefix());
        Transforms->setName(childImg->strippedName());
        if(!childImg->isComplexValued())
        {
            Array<complex<double>,2> kSpace;

            kSpace.resize(childImg->data().rows(),childImg->data().cols());
            real(kSpace) = childImg->data();
            Transforms->ifft(kSpace);
        }
        else
            Transforms->ifft(childImg->dataComplex());
        console->printMessage("iFFT Complete");
    }
    else if(isActiveTable())
    {
        DGVTableVTK *childTbl = activeTable();
        Array<complex<double>,2> kSpace;

        kSpace.resize(childTbl->data().rows(),childTbl->data().cols());
        real(kSpace) = childTbl->data();

        console->printInfo("iFFT on " + childTbl->strippedNamePrefix());
        Transforms->setName(childTbl->strippedName());
        Transforms->ifft(kSpace);
        console->printMessage("iFFT Complete");
    }
    else if(isActiveSPlot())
    {
        DGVSurfacePlotVTK *childSPlt = activeSPlot();
        Array<complex<double>,2> kSpace;

        kSpace.resize(childSPlt->data().rows(),childSPlt->data().cols());
        real(kSpace) = childSPlt->data();

        console->printInfo("iFFT on " + childSPlt->strippedNamePrefix());
        Transforms->setName(childSPlt->strippedName());
        Transforms->ifft(kSpace);
        console->printMessage("iFFT Complete");
    }
    else if(isActiveVPlot())
    {
        console->printError("Not implemented yet");
    }
    done();
}

void DGVMainVTK::FFT_Real()
{
    working();
    conductingFFT = true;
    if(isActiveImage())
    {
        DGVImageVTK *childImg = activeImage();

        console->printInfo("FFT on " + childImg->strippedNamePrefix());
        Transforms->setName(childImg->strippedName());
        Transforms->fft_real(childImg->data());
        console->printMessage("FFT Complete");
    }
    else if(isActiveTable())
    {
        DGVTableVTK *childTbl = activeTable();

        console->printInfo("FFT on " + childTbl->strippedNamePrefix());
        Transforms->setName(childTbl->strippedName());
        Transforms->fft_real(childTbl->data());
        console->printMessage("FFT Complete");
    }
    else if(isActiveSPlot())
    {
        DGVSurfacePlotVTK *childSPlt = activeSPlot();

        console->printInfo("FFT on " + childSPlt->strippedNamePrefix());
        Transforms->setName(childSPlt->strippedName());
        Transforms->fft_real(childSPlt->data());
        console->printMessage("FFT Complete");
    }
    else if(isActiveVPlot())
    {
        console->printError("Not implemented yet");
    }
    done();
}

void DGVMainVTK::iFFT_Real()
{
    working();
    conductingInverseFFT = true;
    if(isActiveImage())
    {
        DGVImageVTK *childImg = activeImage();

        console->printInfo("iFFT on " + childImg->strippedNamePrefix());
        Transforms->setName(childImg->strippedName());
        if(!childImg->isComplexValued())
        {
            Array<complex<double>,2> kSpace;

            kSpace.resize(childImg->data().rows(),childImg->data().cols());
            real(kSpace) = childImg->data();
            Transforms->ifft_real(kSpace);
        }
        else
            Transforms->ifft_real(childImg->dataComplex());
        console->printMessage("iFFT Complete");
    }
    else if(isActiveTable())
    {
        DGVTableVTK *childTbl = activeTable();
        Array<complex<double>,2> kSpace;

        kSpace.resize(childTbl->data().rows(),childTbl->data().cols());
        real(kSpace) = childTbl->data();

        console->printInfo("iFFT on " + childTbl->strippedNamePrefix());
        Transforms->setName(childTbl->strippedName());
        Transforms->ifft_real(kSpace);
        console->printMessage("iFFT Complete");
    }
    else if(isActiveSPlot())
    {
        DGVSurfacePlotVTK *childSPlt = activeSPlot();
        Array<complex<double>,2> kSpace;

        kSpace.resize(childSPlt->data().rows(),childSPlt->data().cols());
        real(kSpace) = childSPlt->data();

        console->printInfo("iFFT on " + childSPlt->strippedNamePrefix());
        Transforms->setName(childSPlt->strippedName());
        Transforms->ifft_real(kSpace);
        console->printMessage("iFFT Complete");
    }
    else if(isActiveVPlot())
    {
        console->printError("Not implemented yet");
    }
    done();
}

void DGVMainVTK::FFT_1D(Array<Array<complex<tableType>,1>,1> data)
{
    working();

    int maxCols = 0;

    for(int j = 0; j < data.size(); j ++)
        if(data(j).size() > maxCols)
            maxCols = data(j).size();

    DGVTableVTK *childTbl = activeTable();
    Array<complex<tableType>,2> values(data.size(),maxCols);

    values = 0;
    for(int j = 0; j < data.size(); j ++)
        for(int k = 0; k < data(j).size(); k ++)
            values(j,k) = data(j)(k);

    console->printInfo("1D FFT on " + childTbl->strippedNamePrefix());
    Transforms->setName(childTbl->strippedName());
    console->printInfo("Assuming multiple selections are of same size. If not, then select individually.");
    conductingFFT_1D = true;
    conductingInverseFFT_1D = false;
    Transforms->fft_1D(values);
    console->printMessage("1D FFT Complete");

    done();
}

void DGVMainVTK::iFFT_1D(Array<Array<complex<tableType>,1>,1> data)
{
    working();

    int maxCols = 0;

    for(int j = 0; j < data.size(); j ++)
        if(data(j).size() > maxCols)
            maxCols = data(j).size();

    DGVTableVTK *childTbl = activeTable();
    Array<complex<tableType>,2> values(data.size(),maxCols);

    values = 0;
    for(int j = 0; j < data.size(); j ++)
        for(int k = 0; k < data(j).size(); k ++)
            values(j,k) = data(j)(k);

    console->printInfo("1D iFFT on " + childTbl->strippedNamePrefix());
    Transforms->setName(childTbl->strippedName());
    console->printInfo("Assuming multiple selections are of same size. If not, then select individually.");
    conductingFFT_1D = false;
    conductingInverseFFT_1D = true;
    Transforms->ifft_1D(values);
    console->printMessage("1D iFFT Complete");

    done();
}

void DGVMainVTK::FFT_Real_1D(Array<Array<tableType,1>,1> data)
{
    working();

    int maxCols = 0;

    for(int j = 0; j < data.size(); j ++)
        if(data(j).size() > maxCols)
            maxCols = data(j).size();

    DGVTableVTK *childTbl = activeTable();
    Array<tableType,2> values(data.size(),maxCols);

    values = 0;
    for(int j = 0; j < data.size(); j ++)
        for(int k = 0; k < data(j).size(); k ++)
            values(j,k) = data(j)(k);

    console->printInfo("1D Real FFT on " + childTbl->strippedNamePrefix());
    Transforms->setName(childTbl->strippedName());
    console->printInfo("Assuming multiple selections are of same size. If not, then select individually.");
    conductingInverseFFT_1D = false;
    Transforms->fft_real_1D(values);
    console->printMessage("1D Real FFT Complete");

    done();
}

void DGVMainVTK::iFFT_Real_1D(Array<Array<complex<tableType>,1>,1> data)
{
    working();

    int maxCols = 0;

    for(int j = 0; j < data.size(); j ++)
        if(data(j).size() > maxCols)
            maxCols = data(j).size();

    DGVTableVTK *childTbl = activeTable();
    Array<complex<tableType>,2> values(data.size(),maxCols);

    values = 0;
    for(int j = 0; j < data.size(); j ++)
        for(int k = 0; k < data(j).size(); k ++)
            values(j,k) = data(j)(k);

    console->printInfo("1D Real iFFT on " + childTbl->strippedNamePrefix());
    Transforms->setName(childTbl->strippedName());
    console->printInfo("Assuming multiple selections are of same size. If not, then select individually.");
    conductingInverseFFT_1D = true;
    Transforms->ifft_real_1D(values);
    console->printMessage("1D Real iFFT Complete");

    done();
}

void DGVMainVTK::radonSlices()
{
    working();
    if(isActiveImage())
    {
        DGVImageVTK *childImg = activeImage();
        console->printInfo("FRT on " + childImg->strippedNamePrefix());
        Transforms->setName(childImg->strippedName());
        if(childImg->isComplexValued())
            Transforms->radonSlices(childImg->dataComplex());
        else
            Transforms->radonSlices(childImg->data());
        console->printMessage("FRT Complete");
    }
    else if(isActiveTable())
    {
        DGVTableVTK *childTbl = activeTable();
        console->printInfo("FRT on " + childTbl->strippedNamePrefix());
        Transforms->setName(childTbl->strippedName());
        if(childTbl->isComplexValued())
            Transforms->radonSlices(childTbl->dataComplex());
        else
            Transforms->radonSlices(childTbl->data());
        console->printMessage("FRT Complete");
    }
    else if(isActiveSPlot())
    {
        console->printError("Not implemented yet");
    }
    else if(isActiveVPlot())
    {
        console->printError("Not implemented yet");
    }
    done();
}

void DGVMainVTK::FRT()
{
    working();
    if(isActiveImage())
    {
        DGVImageVTK *childImg = activeImage();
        console->printInfo("FRT on " + childImg->strippedNamePrefix());
        Transforms->setName(childImg->strippedName());
        Transforms->frt(childImg->data());
        console->printMessage("FRT Complete");
    }
    else if(isActiveTable())
    {
        DGVTableVTK *childTbl = activeTable();
        console->printInfo("FRT on " + childTbl->strippedNamePrefix());
        Transforms->setName(childTbl->strippedName());
        Transforms->frt(childTbl->data());
        console->printMessage("FRT Complete");
    }
    else if(isActiveSPlot())
    {
        DGVSurfacePlotVTK *childSPlt = activeSPlot();
        console->printInfo("FRT on " + childSPlt->strippedNamePrefix());
        Transforms->setName(childSPlt->strippedName());
        Transforms->frt(childSPlt->data());
        console->printMessage("FRT Complete");
    }
    else if(isActiveVPlot())
    {
        console->printError("Not implemented yet");
    }
    done();
}

void DGVMainVTK::iFRT()
{
    working();
    if(isActiveImage())
    {
        DGVImageVTK *childImg = activeImage();
        console->printInfo("iFRT on " + childImg->strippedNamePrefix());
        Transforms->setName(childImg->strippedName());
        Transforms->ifrt(childImg->data());
        console->printMessage("iFRT Complete");
    }
    else if(isActiveTable())
    {
        DGVTableVTK *childTbl = activeTable();
        console->printInfo("iFRT on " + childTbl->strippedNamePrefix());
        Transforms->setName(childTbl->strippedName());
        Transforms->ifrt(childTbl->data());
        console->printMessage("iFRT Complete");
    }
    else if(isActiveSPlot())
    {
        DGVSurfacePlotVTK *childSPlt = activeSPlot();
        console->printInfo("iFRT on " + childSPlt->strippedNamePrefix());
        Transforms->setName(childSPlt->strippedName());
        Transforms->ifrt(childSPlt->data());
        console->printMessage("iFRT Complete");
    }
    else if(isActiveVPlot())
    {
        console->printError("Not implemented yet");
    }
    done();
}

void DGVMainVTK::iFRT_Unscaled()
{
    working();
    if(isActiveImage())
    {
        DGVImageVTK *childImg = activeImage();
        console->printInfo("iFRT (Unscaled) on " + childImg->strippedNamePrefix());
        Transforms->setName(childImg->strippedName());
        Transforms->ifrtUnscaled(childImg->data());
        console->printMessage("iFRT (Unscaled) Complete");
    }
    else if(isActiveTable())
    {
        DGVTableVTK *childTbl = activeTable();
        console->printInfo("iFRT (Unscaled) on " + childTbl->strippedNamePrefix());
        Transforms->setName(childTbl->strippedName());
        Transforms->ifrtUnscaled(childTbl->data());
        console->printMessage("iFRT (Unscaled) Complete");
    }
    else if(isActiveSPlot())
    {
        DGVSurfacePlotVTK *childSPlt = activeSPlot();
        console->printInfo("iFRT (Unscaled) on " + childSPlt->strippedNamePrefix());
        Transforms->setName(childSPlt->strippedName());
        Transforms->ifrtUnscaled(childSPlt->data());
        console->printMessage("iFRT (Unscaled) Complete");
    }
    else if(isActiveVPlot())
    {
        console->printError("Not implemented yet");
    }
    done();
}

void DGVMainVTK::updateWindowMenu()
{
    QWidgetList windows = qobject_cast<QWorkspace *>(workspaces->currentWidget())->windowList();
    QString text;
    DGVImageVTK *childImg = NULL;
    DGVTableVTK *childTbl = NULL;
    DGVSurfacePlotVTK *childSPlt = NULL;
    DGVPlot *childPlt = NULL;
    DGVVolumePlotVTK *childVPlt = NULL;
    QActionGroup *winGp = new QActionGroup(this);
    const int displayLimit = 9;

    menuWindows->clear();
    menuWindows->addAction(actionCascade);
    menuWindows->addAction(actionTile);
    menuWindows->addAction(actionConsole);
    if(console->isVisible())
        actionConsole->setChecked(true);
    else
        actionConsole->setChecked(false);
    menuWindows->addAction(actionSeparator);
    ///\todo Bug: Fix the selecting of the window in menu to be made active.

    for (int j = 0; j < windows.size(); j ++)
    {
        if(!windows.at(j))
            continue;
        childImg = qobject_cast<DGVImageVTK *>(windows.at(j));

        if(childImg == 0)
        {
            childTbl = qobject_cast<DGVTableVTK *>(windows.at(j));

            if(childTbl == 0)
            {
                childSPlt = qobject_cast<DGVSurfacePlotVTK *>(windows.at(j));

                if(childSPlt == 0)
                {
                    childPlt = qobject_cast<DGVPlot *>(windows.at(j));

                    if(childPlt == 0)
                    {
                        childVPlt = qobject_cast<DGVVolumePlotVTK *>(windows.at(j));

                        if(childVPlt == 0)
                            return;
                        else
                        {
                            if(j < displayLimit)
                                text = tr("&%1 %2").arg(j + 1).arg(childVPlt->strippedNamePrefix());
                            else
                                text = tr("%1 %2").arg(j + 1).arg(childVPlt->strippedNamePrefix());
                        }
                    }
                    else
                    {
                        if(j < displayLimit)
                            text = tr("&%1 %2").arg(j + 1).arg(childPlt->strippedNamePrefix());
                        else
                            text = tr("%1 %2").arg(j + 1).arg(childPlt->strippedNamePrefix());
                    }
                }
                else
                {
                    if(j < displayLimit)
                        text = tr("&%1 %2").arg(j + 1).arg(childSPlt->strippedNamePrefix());
                    else
                        text = tr("%1 %2").arg(j + 1).arg(childSPlt->strippedNamePrefix());
                }
            }
            else
            {
                if(j < displayLimit)
                    text = tr("&%1 %2").arg(j + 1).arg(childTbl->strippedNamePrefix());
                else
                    text = tr("%1 %2").arg(j + 1).arg(childTbl->strippedNamePrefix());
            }
        }
        else
        {
            if(j < displayLimit)
                text = tr("&%1 %2").arg(j + 1).arg(childImg->strippedNamePrefix());
            else
                text = tr("%1 %2").arg(j + 1).arg(childImg->strippedNamePrefix());
        }

        QAction *action  = menuWindows->addAction(text);
        action->setCheckable(true);
        winGp->addAction(action);

        if(isActiveImage())
            action ->setChecked(childImg == activeImage());
        else if(isActiveTable())
            action ->setChecked(childTbl == activeTable());
        else if(isActiveSPlot())
            action ->setChecked(childSPlt == activeSPlot());
        else if(isActivePlot())
            action ->setChecked(childPlt == activePlot());
        else if(isActiveVPlot())
            action ->setChecked(childVPlt == activeVPlot());
        else
            action ->setChecked(false);

        connect(action, SIGNAL(triggered()), windowMapper, SLOT(map()));
        windowMapper->setMapping(action, windows.at(j));
    }

    actionSeparator->setVisible(!windows.isEmpty());
    menuWindows->addSeparator();
    menuWindows->addAction(actionCloseActive);
    menuWindows->addAction(actionCloseAll);
}

void DGVMainVTK::setActiveWindow(QWidget *window)
{
    if(!window)
        return;
    else
        qobject_cast<QWorkspace *>(workspaces->currentWidget())->setActiveWindow(window);
}

void DGVMainVTK::setTabName(QWidget *window)
{
    QString tabTitle = activeNamePrefix();
    int index = workspaces->currentIndex();

    if(window == NULL)
        workspaces->setTabText(index, "Empty");
    else
        workspaces->setTabText(index, tabTitle);
}

void DGVMainVTK::setTabName(const QString newName)
{
    int index = workspaces->currentIndex();

    workspaces->setTabText(index, newName);
}

void DGVMainVTK::closeTab(int index)
{
    int newIndex = 0;

    if(workspaces->count() > 1 || index > 0)
    {
        QWorkspace *tmpWorkspace = qobject_cast<QWorkspace *>(workspaces->widget(index));

        //disconnect(tmpWorkspace, 0, this, 0);
        disconnect(tmpWorkspace, SIGNAL(windowActivated(QWidget *)), 0, 0);
        tmpWorkspace->closeAllWindows();
        tmpWorkspace->close();
        workspaces->removeTab(index);

        if(index > 0 && workspaces->currentIndex() == index)
            newIndex = workspaces->currentIndex(); //Safe to decrement
        else if(index > 0)
            newIndex = index-1; //Safe to decrement
        workspaces->setCurrentIndex(newIndex); //else use zero
    }
}

void DGVMainVTK::working()
{
    if(!workingOn)
    {
        QPushButton *btnCancel = new QPushButton("Cancel",this);
        btnCancel->setDisabled(true);

        workingOn = true;
        workingDlg = new QProgressDialog("Busy, Please Wait...", "Cancel", 0, 0, this);
        workingDlg->setAutoClose(false);
        workingDlg->setAutoReset(false);
        workingDlg->setMinimumDuration(1000);
        workingDlg->setCancelButton(btnCancel);
        workingDlg->setWindowModality(Qt::WindowModal);
        workingDlg->show();
        qApp->processEvents();
    }
}

void DGVMainVTK::done()
{
    if(workingOn)
    {
        workingOn = false;
        workingDlg->cancel();
    }
}

void DGVMainVTK::about()
{
    AboutForm *aboutDlg = new AboutForm(this);

    aboutDlg->show();
}

void DGVMainVTK::cut()
{
}

void DGVMainVTK::copy()
{
}

void DGVMainVTK::paste()
{
}

void DGVMainVTK::createMenu()
{
    menuBar = new QMenuBar(this);
    //File
    menuFile = new QMenu(menuBar);
    submenuNew = new QMenu(menuBar);
    menuData = new QMenu(menuBar);
    menuTransform = new QMenu(menuBar);
    submenuFourier = new QMenu(menuBar);
    submenuRadon = new QMenu(menuBar);
    menuWindows = new QMenu(menuBar);
    menuHelp = new QMenu(menuBar);
    actionOpen = new QAction(this);
    actionOpenStack = new QAction(this);
    actionSave = new QAction(this);
    actionSaveRaw = new QAction(this);
    actionSaveStack = new QAction(this);
    actionCloseActive = new QAction(this);
    actionCloseAll = new QAction(this);
    for (int i = 0; i < MaxRecentFiles; ++i)
    {
        actionsRecentFile[i] = new QAction(this);
        actionsRecentFile[i]->setVisible(false);
        connect(actionsRecentFile[i], SIGNAL(triggered()), this, SLOT(openRecentFile()));
    }
    actionExit = new QAction(this);
    actionNewTab = new QAction(this);
    actionNewTable = new QAction(this);
    actionNewImage = new QAction(this);
    //Data
    actionTable = new QAction(this);
    actionEmbed = new QAction(this);
    actionOperate = new QAction(this);
    //Transforms
    actionFFT = new QAction(this);
    actioniFFT = new QAction(this);
    actionFFT_Real = new QAction(this);
    actioniFFT_Real = new QAction(this);
    actionSlice = new QAction(this);
    actionFRT = new QAction(this);
    actioniFRT = new QAction(this);
    actioniFRT_Unscaled = new QAction(this);
    actionNormalise = new QAction(this);
    actionCenterDC = new QAction(this);
    //Window
    actionCascade = new QAction(this);
    actionTile = new QAction(this);
    actionSeparator = new QAction(this);
    actionConsole = new QAction(this);
    //Help
    actionAbout = new QAction(this);

    ///Setup Exit Action and File Menus
    ///File
    menuBar->addAction(menuFile->menuAction());
    menuFile->addAction(submenuNew->menuAction());
    menuFile->setTitle(QApplication::translate("MainWindow", "File", 0, QApplication::UnicodeUTF8));
    submenuNew->setTitle(QApplication::translate("MainWindow", "New", 0, QApplication::UnicodeUTF8));
    actionOpen->setText(QApplication::translate("MainWindow", "Open...", 0, QApplication::UnicodeUTF8));
    actionOpen->setShortcut(tr("Ctrl+o"));
    actionOpenStack->setText(QApplication::translate("MainWindow", "Open Stack...", 0, QApplication::UnicodeUTF8));
    actionOpenStack->setShortcut(tr("Ctrl+Shift+o"));
    actionSave->setText(QApplication::translate("MainWindow", "Save Window as Image...", 0, QApplication::UnicodeUTF8));
    actionSave->setShortcut(tr("Ctrl+s"));
    actionSaveRaw->setText(QApplication::translate("MainWindow", "Save Slice as Native File...", 0, QApplication::UnicodeUTF8));
    actionSaveRaw->setShortcut(tr("Ctrl+Alt+s"));
    actionSaveStack->setText(QApplication::translate("MainWindow", "Save Stack...", 0, QApplication::UnicodeUTF8));
    actionSaveStack->setShortcut(tr("Ctrl+Shift+s"));
    actionCloseActive->setText(QApplication::translate("MainWindow", "Close", 0, QApplication::UnicodeUTF8));
    actionCloseActive->setShortcut(tr("Ctrl+w"));
    actionCloseAll->setText(QApplication::translate("MainWindow", "Close All", 0, QApplication::UnicodeUTF8));
    actionCloseAll->setShortcut(tr("Ctrl+Shift+w"));
    actionExit->setText(QApplication::translate("MainWindow", "Exit", 0, QApplication::UnicodeUTF8));
    actionExit->setShortcut(tr("Ctrl+x"));
    menuFile->addAction(actionOpen);
    menuFile->addAction(actionOpenStack);
    menuFile->addAction(actionSave);
    menuFile->addAction(actionSaveRaw);
    menuFile->addAction(actionSaveStack);
    menuFile->addSeparator();
    menuFile->addAction(actionCloseActive);
    menuFile->addAction(actionCloseAll);
    menuFile->addSeparator();
    for (int i = 0; i < MaxRecentFiles; ++i)
         menuFile->addAction(actionsRecentFile[i]);
    actionRecentFileSeparator = menuFile->addSeparator();
    menuFile->addAction(actionExit);
    updateRecentFileActions();
    ///New
    actionNewTab->setText(QApplication::translate("MainWindow", "New Tab", 0, QApplication::UnicodeUTF8));
    actionNewTab->setShortcut(tr("Alt+t"));
    actionNewTable->setText(QApplication::translate("MainWindow", "New Table", 0, QApplication::UnicodeUTF8));
    actionNewTable->setShortcut(tr("Alt+n"));
    actionNewImage->setText(QApplication::translate("MainWindow", "New Image", 0, QApplication::UnicodeUTF8));
    actionNewImage->setShortcut(tr("Ctrl+n"));
    submenuNew->addAction(actionNewTab);
    submenuNew->addAction(actionNewTable);
    submenuNew->addAction(actionNewImage);
    ///Data
    menuBar->addAction(menuData->menuAction());
    menuData->setTitle(QApplication::translate("MainWindow", "Data", 0, QApplication::UnicodeUTF8));
    actionTable->setText(QApplication::translate("MainWindow", "Table of View", 0, QApplication::UnicodeUTF8));
    actionTable->setShortcut(tr("Ctrl+Shift+t"));
    menuData->addSeparator();
    actionEmbed->setText(QApplication::translate("MainWindow", "Embed/Crop...", 0, QApplication::UnicodeUTF8));
    actionEmbed->setDisabled(true);
    actionOperate->setText(QApplication::translate("MainWindow", "Operate...", 0, QApplication::UnicodeUTF8));
    actionOperate->setShortcut(tr("Ctrl+Shift+o"));
    menuData->addAction(actionTable);
    menuData->addAction(actionEmbed);
    menuData->addAction(actionOperate);
    ///Transform
    menuBar->addAction(menuTransform->menuAction());
    menuTransform->setTitle(QApplication::translate("MainWindow", "Transform", 0, QApplication::UnicodeUTF8));
    menuTransform->addAction(submenuFourier->menuAction());
    menuTransform->addAction(submenuRadon->menuAction());
    submenuFourier->setTitle(QApplication::translate("MainWindow", "Fourier", 0, QApplication::UnicodeUTF8));
    submenuRadon->setTitle(QApplication::translate("MainWindow", "Radon", 0, QApplication::UnicodeUTF8));
    actionFFT->setText(QApplication::translate("MainWindow", "FFT", 0, QApplication::UnicodeUTF8));
    actionFFT->setShortcut(tr("F7"));
    actioniFFT->setText(QApplication::translate("MainWindow", "iFFT", 0, QApplication::UnicodeUTF8));
    actioniFFT->setShortcut(tr("Shift+F7"));
    actionFFT_Real->setText(QApplication::translate("MainWindow", "Real FFT", 0, QApplication::UnicodeUTF8));
    actionFFT_Real->setShortcut(tr("F8"));
    actioniFFT_Real->setText(QApplication::translate("MainWindow", "Real iFFT", 0, QApplication::UnicodeUTF8));
    actioniFFT_Real->setShortcut(tr("Shift+F8"));
    actionSlice->setText(QApplication::translate("MainWindow", "Radon Slices", 0, QApplication::UnicodeUTF8));
    actionSlice->setShortcut(tr("Ctrl+Shift+F8"));
    actionNormalise->setText(QApplication::translate("MainWindow", "Normalise", 0, QApplication::UnicodeUTF8));
    actionNormalise->setShortcut(tr("Ctrl+Shift+F7"));
    actionNormalise->setCheckable(true);
    actionNormalise->setChecked(true);
    actionCenterDC->setText(QApplication::translate("MainWindow", "Center DC", 0, QApplication::UnicodeUTF8));
    actionCenterDC->setShortcut(tr("Ctrl+F7"));
    actionCenterDC->setCheckable(true);
    actionCenterDC->setChecked(false);
    actionFRT->setText(QApplication::translate("MainWindow", "FRT", 0, QApplication::UnicodeUTF8));
    actionFRT->setShortcut(tr("F9"));
    actioniFRT->setText(QApplication::translate("MainWindow", "iFRT", 0, QApplication::UnicodeUTF8));
    actioniFRT->setShortcut(tr("Shift+F9"));
    actioniFRT_Unscaled->setText(QApplication::translate("MainWindow", "iFRT Unscaled", 0, QApplication::UnicodeUTF8));
    actioniFRT_Unscaled->setShortcut(tr("Ctrl+Shift+F9"));
    submenuFourier->addAction(actionFFT);
    submenuFourier->addAction(actioniFFT);
    submenuFourier->addAction(actionFFT_Real);
    submenuFourier->addAction(actioniFFT_Real);
    submenuFourier->addAction(actionSlice);
    submenuFourier->addAction(actionNormalise);
    submenuFourier->addAction(actionCenterDC);
    submenuRadon->addAction(actionFRT);
    submenuRadon->addAction(actioniFRT);
    submenuRadon->addAction(actioniFRT_Unscaled);
    ///Windows
    menuBar->addAction(menuWindows->menuAction());
    menuWindows->setTitle(QApplication::translate("MainWindow", "Windows", 0, QApplication::UnicodeUTF8));
    actionCascade->setText(QApplication::translate("MainWindow", "Cascade", 0, QApplication::UnicodeUTF8));
    menuWindows->addAction(actionCascade);
    actionTile->setText(QApplication::translate("MainWindow", "Tile", 0, QApplication::UnicodeUTF8));
    menuWindows->addAction(actionTile);
    actionSeparator->setSeparator(true);
    actionConsole->setText(QApplication::translate("MainWindow", "Console", 0, QApplication::UnicodeUTF8));
    actionConsole->setCheckable(true);
    if(console->isVisible())
        actionConsole->setChecked(true);
    else
        actionConsole->setChecked(false);
    menuWindows->addAction(actionConsole);
    menuWindows->addAction(actionSeparator);
    updateWindowMenu();
    connect(menuWindows, SIGNAL(aboutToShow()), this, SLOT(updateWindowMenu()));
    ///Help
    menuBar->addAction(menuHelp->menuAction());
    menuHelp->setTitle(QApplication::translate("MainWindow", "Help", 0, QApplication::UnicodeUTF8));
    actionAbout->setText(QApplication::translate("MainWindow", "About", 0, QApplication::UnicodeUTF8));
    actionAbout->setShortcut(tr("Shift+F1"));
    menuHelp->addAction(actionAbout);

    ///Setup Vector to Pass as Context Menus to children
    contextMenusToPass.append(menuTransform);

    ///Set tooltips
    setupTooltips();

    statusBar()->showMessage(tr("Ready"));
    setMenuBar(menuBar);
}

void DGVMainVTK::createConnections()
{
    connect(workspaces, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int)));
    //connect(windowMapper, SIGNAL(mapped(QWidget *)), this, SLOT(setActiveWindow(QWidget *)));

    qRegisterMetaType< Array<imageType,2> >("Array<imageType,2>"); ///Need for ensuring cross thread signalling
    qRegisterMetaType< Array< complex<imageType>,2 > >("Array< complex<imageType>,2 >"); ///Need for ensuring cross thread signalling
    connect(Transforms, SIGNAL(resultAvailable(Array<imageType,2> , QString)), this, SLOT(createImage(Array<imageType,2> , QString)));
    connect(Transforms, SIGNAL(resultAvailable(Array< complex<imageType>,2 > , QString)), this, SLOT(createImage(Array< complex<imageType>,2 > , QString)));
    connect(Transforms, SIGNAL(resultAvailable_1D(Array<imageType,2> , QString)), this, SLOT(createTable(Array<imageType,2> , QString)));
    connect(Transforms, SIGNAL(resultAvailable_1D(Array< complex<imageType>,2 > , QString)), this, SLOT(createTable(Array< complex<imageType>,2 > , QString)));

    QObject::connect(actionOpen, SIGNAL(activated()), this, SLOT(open()));
    QObject::connect(actionOpenStack, SIGNAL(activated()), this, SLOT(openStack()));
    QObject::connect(actionSave, SIGNAL(activated()), this, SLOT(save()));
    QObject::connect(actionSaveRaw, SIGNAL(activated()), this, SLOT(saveNative()));
    QObject::connect(actionSaveStack, SIGNAL(activated()), this, SLOT(saveStack()));
    QObject::connect(actionCloseActive, SIGNAL(activated()), this, SLOT(closeTabActiveWindow()));
    QObject::connect(actionCloseAll, SIGNAL(activated()), this, SLOT(closeTabAllWindows()));
    QObject::connect(actionExit, SIGNAL(activated()), this, SLOT(close()));
    QObject::connect(actionNewTab, SIGNAL(activated()), this, SLOT(newTab()));
    QObject::connect(actionNewTable, SIGNAL(activated()), this, SLOT(newTable()));
    QObject::connect(actionNewImage, SIGNAL(activated()), this, SLOT(newImage()));
    //Data
    QObject::connect(actionTable, SIGNAL(activated()), this, SLOT(table()));
    QObject::connect(actionEmbed, SIGNAL(activated()), this, SLOT(embed()));
    QObject::connect(actionOperate, SIGNAL(activated()), this, SLOT(operate()));
    //Transform
    QObject::connect(actionFFT, SIGNAL(activated()), this, SLOT(FFT()));
    QObject::connect(actioniFFT, SIGNAL(activated()), this, SLOT(iFFT()));
    QObject::connect(actionFFT_Real, SIGNAL(activated()), this, SLOT(FFT_Real()));
    QObject::connect(actioniFFT_Real, SIGNAL(activated()), this, SLOT(iFFT_Real()));
    QObject::connect(actionSlice, SIGNAL(activated()), this, SLOT(radonSlices()));
    QObject::connect(actionFRT, SIGNAL(activated()), this, SLOT(FRT()));
    QObject::connect(actioniFRT, SIGNAL(activated()), this, SLOT(iFRT()));
    QObject::connect(actioniFRT_Unscaled, SIGNAL(activated()), this, SLOT(iFRT_Unscaled()));
    QObject::connect(actionCascade, SIGNAL(activated()), this, SLOT(cascadeTab()));
    QObject::connect(actionTile, SIGNAL(activated()), this, SLOT(tileTab()));
    QObject::connect(actionConsole, SIGNAL(triggered(bool)), console, SLOT(setVisible(bool)));
    QObject::connect(actionAbout, SIGNAL(activated()), this, SLOT(about()));
}

void DGVMainVTK::contextMenuEvent(QContextMenuEvent *event)
{
    contextMenu = new QMenu(this); //!< Only exists for the duration of the context selection

    contextMenu->addAction(submenuNew->menuAction());
    contextMenu->addAction(actionOpen);
    contextMenu->addAction(actionSave);
    contextMenu->addAction(actionSaveRaw);
    contextMenu->addSeparator();
    contextMenu->addAction(actionCloseActive);
    contextMenu->addAction(actionCloseAll);
    contextMenu->addSeparator();
    contextMenu->addAction(actionCascade);
    contextMenu->addAction(actionTile);
    contextMenu->addSeparator();
    contextMenu->addAction(actionExit);

    contextMenu->exec(event->globalPos());
}

void DGVMainVTK::dragEnterEvent(QDragEnterEvent *event)
{
    if(event->mimeData()->hasFormat("text/uri-list") || event->mimeData()->hasFormat("text/plain"))
        event->acceptProposedAction();
}

void DGVMainVTK::dropEvent(QDropEvent *event)
{
    QList<QUrl> urlsList = event->mimeData()->urls();
    QString tmp;

    for(int j = 0; j < urlsList.size(); j ++)
    {
        if(urlsList[j].isValid())
        {
            #ifdef Q_WS_WIN
                tmp = urlsList[j].path().remove(0,1); //!< Remove leading forward slash
                console->printInfo("Dropped Path (Win): " + tmp);
                loadFile(tmp);
            #else
                console->printInfo("Dropped Path (Linux): " + urlsList[j].path());
                loadFile(urlsList[j].path());
            #endif
        }
    }

    event->acceptProposedAction();
}

void DGVMainVTK::closeEvent(QCloseEvent *event)
{
    writeSettings();
    event->accept();
}

void DGVMainVTK::setupTooltips()
{
    actionSave->setToolTip("Save the state of the window (i.e., exactly how it looks) as an image");
    actionSaveRaw->setToolTip("Save the data in exact form with the DGV Native Format. Saves complex and non-complex valued data");
    actionTable->setToolTip("Display a table of the data being viewed");
    actionOperate->setToolTip("Compute aritmetic with data currently open");
    actionTable->setToolTip("Display Table of the data being viewed, which may be different from entire data (e.g. complex data)");
    actionEmbed->setToolTip("Embed/Crop the data");
    actionOperate->setToolTip("Apply Arithmetic Operation to data");
    actionFFT->setStatusTip("Complex-valued Fast Fourier Transform (FFT)");
    actioniFFT->setToolTip("Complex-valued Inverse Fast Fourier Transform (iFFT)");
    actioniFFT->setStatusTip("Complex-valued Inverse Fast Fourier Transform (iFFT)");
    actionFFT_Real->setToolTip("Real-valued Fast Fourier Transform (FFT)");
    actionFFT_Real->setStatusTip("Real-valued Fast Fourier Transform (FFT)");
    actioniFFT_Real->setToolTip("Real-valued Inverse Fast Fourier Transform (iFFT)");
    actioniFFT_Real->setStatusTip("Real-valued Inverse Fast Fourier Transform (iFFT)");
    actionSlice->setToolTip("Extract All Radon Slices via the Fourier Slice Theorem");
    actionSlice->setStatusTip("Extract All Radon Slices via the Fourier Slice Theorem");
    actionFRT->setToolTip("Fast Finite Radon Transform (FRT)");
    actionFRT->setStatusTip("Fast Finite Radon Transform (FRT)");
    actioniFRT->setToolTip("Inverse Fast Finite Radon Transform (iFRT)");
    actioniFRT->setStatusTip("Inverse Fast Finite Radon Transform (iFRT)");
    actioniFRT_Unscaled->setToolTip("Unscaled Inverse Fast Finite Radon Transform (iFRT Unscaled)");
    actioniFRT_Unscaled->setStatusTip("Unscaled Inverse Fast Finite Radon Transform (iFRT Unscaled)");
}

//From Qt Examples for Recent Files
void DGVMainVTK::setCurrentFile(const QString &fileName)
{
    QFileInfo fi(fileName);

    if(!fileName.isEmpty())
        setWindowTitle(tr("%1 - %2").arg(strippedName(fileName)).arg(tr("DGV")));
    else
        setWindowTitle(tr("Discrete Geometry Viewer"));

    QSettings settings("Shakes", "DGV");
    QStringList files = settings.value("recentFileList").toStringList();
    files.removeAll(fileName);
    files.prepend(fileName);
    while (files.size() > MaxRecentFiles)
        files.removeLast();

    settings.setValue("recentFileList", files);
    settings.setValue("recentPath",fi.absolutePath());

    foreach (QWidget *widget, QApplication::topLevelWidgets())
    {
        DGVMainVTK *mainWin = qobject_cast<DGVMainVTK *>(widget);
        if (mainWin)
            mainWin->updateRecentFileActions();
    }
}

//From Qt Examples for Recent Files
void DGVMainVTK::updateRecentFileActions()
{
    QSettings settings("Shakes", "DGV");
    QStringList files = settings.value("recentFileList").toStringList();

    int numRecentFiles = qMin(files.size(), (int)MaxRecentFiles);

    for (int i = 0; i < numRecentFiles; ++i) {
        QString text = tr("&%1 %2").arg(i + 1).arg(strippedName(files[i]));
        actionsRecentFile[i]->setText(text);
        actionsRecentFile[i]->setData(files[i]);
        actionsRecentFile[i]->setToolTip(files[i]);
        actionsRecentFile[i]->setStatusTip(files[i]);
        actionsRecentFile[i]->setShortcut("Ctrl+" + text.setNum( (i+1)%numRecentFiles ));
        actionsRecentFile[i]->setVisible(true);
    }
    for (int j = numRecentFiles; j < MaxRecentFiles; ++j)
        actionsRecentFile[j]->setVisible(false);

    actionRecentFileSeparator->setVisible(numRecentFiles > 0);
}

void DGVMainVTK::commonChildProperties(QWidget *widget)
{
    widget->setAttribute(Qt::WA_DeleteOnClose);
    widget->setFocusPolicy(Qt::StrongFocus);
}

bool DGVMainVTK::isActiveImage()
{
    if(activeImage() == 0)
        return false;
    else
        return true;
}

DGVImageVTK* DGVMainVTK::activeImage()
{
    if(QWidget *activeWin = qobject_cast<QWorkspace *>(workspaces->currentWidget())->activeWindow())
        return qobject_cast<DGVImageVTK *>(activeWin);
    return 0;
}

bool DGVMainVTK::isActiveTable()
{
    if(activeTable() == 0)
        return false;
    else
        return true;
}

DGVTableVTK* DGVMainVTK::activeTable()
{
    if(QWidget *activeWin = qobject_cast<QWorkspace *>(workspaces->currentWidget())->activeWindow())
        return qobject_cast<DGVTableVTK *>(activeWin);
    return 0;
}

bool DGVMainVTK::isActiveSPlot()
{
    if(activeSPlot() == 0)
        return false;
    else
        return true;
}

DGVSurfacePlotVTK* DGVMainVTK::activeSPlot()
{
    if(QWidget *activeWin = qobject_cast<QWorkspace *>(workspaces->currentWidget())->activeWindow())
        return qobject_cast<DGVSurfacePlotVTK *>(activeWin);
    return 0;
}

bool DGVMainVTK::isActivePlot()
{
    if(activePlot() == 0)
        return false;
    else
        return true;
}

DGVPlot* DGVMainVTK::activePlot()
{
    if(QWidget *activeWin = qobject_cast<QWorkspace *>(workspaces->currentWidget())->activeWindow())
        return qobject_cast<DGVPlot *>(activeWin);
    return 0;
}

bool DGVMainVTK::isActiveVPlot()
{
    if(activeVPlot() == 0)
        return false;
    else
        return true;
}

DGVVolumePlotVTK* DGVMainVTK::activeVPlot()
{
    if(QWidget *activeWin = qobject_cast<QWorkspace *>(workspaces->currentWidget())->activeWindow())
        return qobject_cast<DGVVolumePlotVTK *>(activeWin);
    return 0;
}

bool DGVMainVTK::isActiveLattice()
{
    if(activeLattice() == 0)
        return false;
    else
        return true;
}

DGVLatticeVTK* DGVMainVTK::activeLattice()
{
    if(QWidget *activeWin = qobject_cast<QWorkspace *>(workspaces->currentWidget())->activeWindow())
        return qobject_cast<DGVLatticeVTK *>(activeWin);
    return 0;
}

void DGVMainVTK::writeSettings()
 {
     QSettings settings("Shakes", "DGV");

     settings.beginGroup("DGVWindow");
     settings.setValue("size", size());
     settings.setValue("pos", pos());
     settings.endGroup();
 }

 void DGVMainVTK::readSettings()
 {
     QSettings settings("Shakes", "DGV");

     settings.beginGroup("DGVWindow");
     resize(settings.value("size", QSize(400, 400)).toSize());
     move(settings.value("pos", QPoint(200, 200)).toPoint());
     settings.endGroup();
 }
